[CrashReporter] Enable InProc CrashReporter for crashing GC threads - #131821
[CrashReporter] Enable InProc CrashReporter for crashing GC threads#131821mdh1418 wants to merge 3 commits into
Conversation
Reuse an existing suspension only when the crashing thread owns it, and allow workstation background GC to perform reporter-owned suspend and resume while preserving Server GC deadlock safeguards. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa976a9e-b875-4524-82e1-1485d03d14fa
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR updates CoreCLR’s in-proc crash-report stack walker to allow managed thread enumeration in additional GC/suspension scenarios by tracking whether a runtime suspension is (a) unavailable, (b) already established and owned by the crashing thread, or (c) created by the crash reporter.
Changes:
- Introduces
CrashReportSuspensionOwnershipto distinguish between an unusable suspension, a usable existing suspension, and a reporter-created suspension. - Reuses an existing completed suspension only when the crashing thread owns the ThreadStore lock; otherwise avoids enumerating other managed threads.
- Resumes the runtime only when the crash reporter itself performed the suspension.
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa976a9e-b875-4524-82e1-1485d03d14fa
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (3)
src/coreclr/vm/crashreportstackwalker.cpp:601
- ThreadStore::GetThreadList() requires the current thread to hold the ThreadStore lock (it asserts s_pThreadStore->m_Crst.GetEnterCount() > 0). With the new CrashReportSuspensionOwnership::Existing path (e.g., stable Server GC worker where another thread owns the lock), this loop can trip that assert in checked builds and violates the existing ThreadStore locking contract.
To make the 'Existing but not owned by this thread' scenario safe, either (a) avoid enumerating other threads unless the current thread holds the ThreadStore lock, or (b) introduce an explicit "enumerate thread list while another thread holds the lock / SysIsSuspended" helper that does not require lock ownership, and use that here.
if (suspensionOwnership != CrashReportSuspensionOwnership::Unavailable)
{
Thread* pThread = nullptr;
while ((pThread = ThreadStore::GetThreadList(pThread)) != nullptr)
{
src/coreclr/vm/crashreportstackwalker.cpp:534
- The comment says we should avoid calling SuspendEE when a suspension is already in progress, but the code doesn't check SysIsSuspendInProgress(). As a result, the crash reporter can still call SuspendEE while another thread is mid-suspension, which risks blocking on the ThreadStore lock (exactly what the comment says to avoid).
if (g_fFatalErrorOccurredOnGCThread
|| GCHeapUtilities::IsGCInProgress()
|| crashThreadOwnsSuspension)
{
return CrashReportSuspensionOwnership::Unavailable;
}
src/coreclr/inc/gccrashreport.h:12
- This introduces a direct VM->GC call surface (SVR::IsGCThreadUsingStableSuspension) that bypasses the existing standalone-GC loading/probing mechanism (GC_VersionInfo/GC_Initialize via direct link vs GetProcAddress). In builds that load a standalone GC, this symbol may not exist / may not be resolvable the same way, and even if it does, calling it directly would not follow the selected GC module.
Consider plumbing this through the same mechanism as other GC entrypoints (an extern "C" exported function resolved during GC load and stored as a function pointer, or an existing GC interface contract) and make the header comment match the actual behavior/availability guarantees.
// Crash reporting entry points implemented by the linked-in GC. Standalone GCs
// conservatively return false because they do not execute this linked-in GC code.
namespace SVR
{
bool IsGCThreadUsingStableSuspension();
}
Track blocking Server GC participation through the final join so the in-process crash reporter can safely reuse the existing suspension without racing RestartEE. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aa976a9e-b875-4524-82e1-1485d03d14fa
ff8562c to
964cdd9
Compare
There was a problem hiding this comment.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Note
This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.
| { | ||
| // Foreground Server GC workers are non-suspendable GC-special threads. | ||
| // Suspendable background GC threads have an associated EE Thread. | ||
| bool isServerGCWorker = IsGCSpecialThread() && pCrashThread == nullptr; |
There was a problem hiding this comment.
Is pCrashThread always current thread? If so, maybe we should enforce it since we use API to check current thread type together with state of pCrashThread. Alternative is not to do this if pCrashThread is not current thread.
Previously, the InProc CrashReporter skipped suspending and enumerating other managed threads during any GC scenario. However, in select GC scenarios, the thread running the reporter either owns the existing thread suspension or can safely establish its own suspension and walk managed call stacks.
This change tracks suspension ownership so the reporter can: